//
//  SimRequest.m
//  TKnowBox
//
//  Created by LiuXubin on 16/5/7.
//  Copyright © 2016年 knowin. All rights reserved.
//

#import "SimRequest.h"
#import "SimLoading.h"
#import "SimCommonTool.h"

static dispatch_queue_t dispatchQueue;
static NSMutableSet *requestURLKeys;

NSString * const kLoadingType = @"kLoadingType";
NSString * const kLoadingView = @"kLoadingView";
NSString * const kReturnType = @"kReturnType";

SimDefineValue(kSimRequestServerAddress, NSString *);

SimImpValue(kSimRequestServerAdrress, NSString *);
SimImpValue(kSimRequestWebServerAddress, NSString *);
SimImpValue(kSimRequestBaseInfoDict, NSDictionary *);
SimImpValue(kSimRequestCacheDir, NSString *);
SimImpValue(kSimRequestTimeOut, int);

@implementation SimRequest

+ (void)setServerAddress:(NSString *)address
{
    SimSetValue(kSimRequestServerAdrress, address);
}

+ (NSString *)serverAddress
{
    return SimValue(kSimRequestServerAdrress, nil);
}

+ (void)setWebServerAddress:(NSString *)address
{
    SimSetValue(kSimRequestWebServerAddress, address);
}

+ (NSString *)webServerAddress
{
    return SimValue(kSimRequestWebServerAddress, nil);
}

+ (NSURLSessionDataTask *)requestPath:(NSString *)path
                            loadingDic:(NSDictionary *)loadingDic
                            postParam:( NSDictionary * _Nullable )postParam
                             getParam:( NSDictionary * _Nullable )getParam
                                 cache:(BOOL)cache
                               success:(void (^)(SimRequest *request,  id jsonDict))success
                               failure:(void (^)(SimRequest *request, NSError *error))failure;
{
    
    //request
    NSString *serverAdr = [self serverAddress];
    if (!serverAdr) {
        return nil;
    }

    SimRequest *manager = [self manager];
    [manager.requestSerializer setTimeoutInterval:SimValue(kSimRequestTimeOut, 60)];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.requestSerializer=[AFJSONRequestSerializer serializer];


    NSString *urlPath = [serverAdr stringByAppendingString:path];
    NSMutableDictionary *queryParams = nil;
    NSMutableDictionary *postParams = nil;
    NSString *cacheKeyStr = urlPath;
    NSDictionary *baseInfoDict = [self baseInfoDict:getParam];
    if (baseInfoDict) {
        queryParams = [NSMutableDictionary dictionaryWithDictionary:baseInfoDict];
    }
    else{
        queryParams = [NSMutableDictionary dictionary];
    }
    NSString *queryStr = [manager queryStrAddSaltForQueryParam:queryParams];
    cacheKeyStr = [cacheKeyStr stringByAppendingString:queryStr];
    if ([postParam allKeys].count > 0) {
        urlPath = [urlPath stringByAppendingFormat:@"?%@", queryStr];
        
        postParams = [NSMutableDictionary dictionaryWithDictionary:postParam];
        NSString *postStr = [manager postStrAddSaltForPostParam:postParams];
        cacheKeyStr = [cacheKeyStr stringByAppendingString:postStr];
    }
    
    //Cache
    NSString *cacheKey = cacheKeyStr.length > 0 ? [self keyForURLString:cacheKeyStr] : nil;
    if (postParam.count > 0) {
        if ([requestURLKeys containsObject:cacheKey]) {
            return nil;
        }
        else{
            if (!requestURLKeys) {
                requestURLKeys = [[NSMutableSet alloc] init];
            }
            [requestURLKeys addObject:cacheKey];
        }
    }
    
    
    CacheDataReturnType returnType = loadingDic[kReturnType] ? [loadingDic[kReturnType] integerValue] : CDRT_FailureReturn;
    
    //Loading
    RequestLoadType loadingType = loadingDic ? [loadingDic[kLoadingType] integerValue] : RLT_None;
    UIView *loadingView = loadingDic[kLoadingView];
    
    if(loadingType != RLT_None){
        [self showLoadingForType:loadingType inView:loadingView];
    }
    
    BOOL hasReturnCacheData = NO;
    if (cache && cacheKeyStr.length > 0) {
        if (returnType == CDRT_ImmediateReturn) {
            NSDictionary *jsonDict = [self cacheDictForURLKey:cacheKey];
            if (jsonDict) {
                if(loadingType != RLT_None){
                    [self dismissLoadingInView:loadingView];
                }
                if (success) {
                    success(manager, [[self class] handleSuccessJson:jsonDict]);
                    hasReturnCacheData = YES;
                }
            }
        }
    }
    
    void (^SuccessBlock)(NSURLSessionDataTask *, id) = ^(NSURLSessionDataTask *operation, id responseObject){
        [requestURLKeys removeObject:cacheKey];
        if(loadingType != RLT_None){
            [self dismissLoadingInView:loadingView];
        }
        NSDictionary *jsonDict = nil;
        if ([responseObject isKindOfClass:[NSDictionary class]]) {
            jsonDict = responseObject;
        }
#ifndef __OPTIMIZE__
        NSLog(@"%@ jsonDict:%@", path, jsonDict);
#endif
        if ([[self class] isSuccessJsonForJsonDict:jsonDict]) {
            if (cache) {
                //save to cache
                [manager saveResponseDict:jsonDict forURLKey:cacheKey];
            }
            
            if(success){
                success(manager, [[self class] handleSuccessJson:jsonDict]);
            }
        }
        else{
            if ([[self class] isValidateJsonForJsonDict:jsonDict]) {
                BOOL bContinue = [self dealWithErrorCodeJson:jsonDict];
                if (bContinue) {
                    if(failure){
                        failure(manager, [self errorForValidateJson:jsonDict]);
                    }
                }
            }
            else{
                if(failure){
                    failure(manager,  [self errorForValidateJson:jsonDict]);
                }
            }
        }
    };
    
    void (^FailureBlock)(NSURLSessionDataTask *, NSError *) = ^(NSURLSessionDataTask *operation, NSError *error){
        [requestURLKeys removeObject:cacheKey];
        if(loadingType != RLT_None){
            [self dismissLoadingInView:loadingView];
        }
        if (hasReturnCacheData) { //已经返回缓存数据，失败不作处理
            return;
        }
        
        NSDictionary *jsonDict = nil;
        if (cache) {
            //user cache
            jsonDict = [self cacheDictForURLKey:cacheKey];
        }
        
        if ([[self class] isSuccessJsonForJsonDict:jsonDict]) {
            if(success){
#ifndef __OPTIMIZE__
                NSLog(@"***User Cahce ** %@ \njsonDict:%@", path, jsonDict);
                success(manager, [[self class] handleSuccessJson:jsonDict]);
#endif
            }
        }
        else{
            if (failure) {
                failure(manager, error);
            }
        }
    };
    
    NSURLSessionDataTask *operation = nil;
    if (postParams) {
        operation = [manager POST:urlPath parameters:postParams progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            SuccessBlock(operation, responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            FailureBlock(operation, error);
        }];
    }
    else{
        operation = [manager GET:urlPath parameters:queryParams progress:NULL success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            SuccessBlock(task, responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            FailureBlock(task, error);
        }];
    }
    
    return operation;
}

+ (NSURLSessionDataTask *)requestPath:(NSString *)path
                            loadingDic:(NSDictionary * _Nullable)loadingDic
                            queryParam:(NSDictionary * _Nullable)param
                                 cache:(BOOL)cache
                               success:(void (^)(SimRequest *request,  id jsonDict))success
                               failure:(void (^)(SimRequest *request, NSError *error))failure
{
    return [self requestPath:path
                 loadingDic:loadingDic
                  postParam:nil
                   getParam:param
                      cache:cache
                    success:success
                    failure:failure];
}


+ (NSURLSessionDataTask *)requestPath:(NSString *)path
                            loadingDic:(NSDictionary * _Nullable)loadingDic
                            queryParam:(NSDictionary * _Nullable)param
                               success:(void (^)(SimRequest *request,  id jsonDict))success
                               failure:(void (^)(SimRequest *request, NSError *error))failure
{
    return [self requestPath:path
                 loadingDic:loadingDic
                 queryParam:param
                      cache:NO
                    success:success
                    failure:failure];
}

+ (NSURLSessionDataTask *)requestPath:(NSString *)path
                            loadingDic:(NSDictionary * _Nullable)loadingDic
                             postParam:(NSDictionary * _Nullable)param
                               success:(void (^)(SimRequest *request,  id jsonDict))success
                               failure:(void (^)(SimRequest *request, NSError *error))failure
{
    return [self requestPath:path
                 loadingDic:loadingDic
                  postParam:param
                   getParam:nil
                      cache:NO
                    success:success
                    failure:failure];
}

#pragma mark - download
+ (NSURLSessionDownloadTask *)download:(NSString *)urlStr toPath:(NSString *)filePath progressBlock:(void (^)(NSProgress * _Nonnull downloadProgress))grogressBlock complete:(CompleteBlock)complete
{
    NSURL *url = [NSURL URLWithString:urlStr];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
    
    //   AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    
    NSURLSessionDownloadTask *task = [sessionManager downloadTaskWithRequest:request progress:grogressBlock destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        
        return [NSURL fileURLWithPath:filePath];
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        
        if (error) {
            if (complete) {
                complete(NO);
            }
        }else {
            if (complete) {
                complete(YES);
            }
        }
        
    }];
    
    //开始启动任务
    [task resume];
    return task;
}

#pragma mark - custom
- (NSString *)queryStrAddSaltForQueryParam:(NSMutableDictionary *)queryParams
{
    NSString *queryStr = AFQueryStringFromParameters(queryParams);
    NSString *saltStr = [queryStr stringByAppendingString:@"1qaz2wsx"];
    queryParams[@"kbparam"] = mdsString(saltStr);

    queryStr = AFQueryStringFromParameters(queryParams);
    return queryStr;
}

- (NSString *)postStrAddSaltForPostParam:(NSMutableDictionary *)postParams
{
    postParams[@"kbparam"] = @""; //模拟kbparam内容，保持顺序一致，修复NSJSONSerialization的问题
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postParams
                                                       options:[(AFJSONRequestSerializer *)self.requestSerializer writingOptions]
                                                         error:nil];
    NSString *postStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    
    ////删除模拟kbparam内容生成的字符串
    NSRange kbparamRange = [postStr rangeOfString:@",\"kbparam\":\"\""];
    if (kbparamRange.location == NSNotFound) {
        kbparamRange = [postStr rangeOfString:@"\"kbparam\":\"\","];
    }
    if (kbparamRange.location != NSNotFound) {
        postStr = [postStr stringByReplacingCharactersInRange:kbparamRange withString:@""];
    }
    
    NSString *saltStr = [postStr stringByAppendingString:@"123qwe!@#"];
    postParams[@"kbparam"] = mdsString(saltStr);
    
    return postStr;
}


+ (BOOL)isValidateJsonForJsonDict:(nullable id)jsonDict
{
    return YES;
}

+ (BOOL)isSuccessJsonForJsonDict:(nullable id)jsonDict
{
    return YES;
}

+ (nullable NSDictionary *)baseInfoDict:(nullable NSDictionary *)dict;
{
    return SimValue(kSimRequestBaseInfoDict, nil);
}

+ (id)handleSuccessJson:(nullable id)json
{
    return nil;
}

+ (NSError *)errorForValidateJson:(nullable id)json
{
    return [[NSError alloc] initWithDomain:@"SimRequestError" code:-1 userInfo:json];
}

+ (BOOL)dealWithErrorCodeJson:(nullable id)json
{
    return YES;
}


#pragma mark - cache
+ (NSString *)dirForCacheReqeust
{
    NSString *cacheDir = SimValue(kSimRequestCacheDir, nil);
    if (cacheDir.length == 0) {
        NSString *cachePath =  [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        cacheDir = [cachePath stringByAppendingPathComponent:@"SimRequestCache"];
        SimSetValue(kSimRequestCacheDir, cacheDir);
    }
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:cacheDir]){
        [[NSFileManager defaultManager] createDirectoryAtPath:cacheDir
                                  withIntermediateDirectories:YES
                                                   attributes:nil
                                                        error:NULL];
    }

    return cacheDir;
}

+ (void)clearCache
{
    NSString *cacheDir = SimValue(kSimRequestCacheDir, nil);
    if (cacheDir) {
        [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:nil];
        cacheDir = nil;
    }
}

+ (NSString *)cachePathForURLKey:(NSString *)URLKey
{
    return [[self dirForCacheReqeust] stringByAppendingPathComponent:URLKey];
}


+ (NSDictionary *)cacheDictForURLKey:(NSString *)URLKey
{
    NSString *cachePath = [self cachePathForURLKey:URLKey];
    return [NSDictionary dictionaryWithContentsOfFile:cachePath];
}

- (void)saveResponseDict:(NSDictionary *)responseDict forURLKey:(NSString *)URLKey
{
    NSString *cachePath = [[self class] cachePathForURLKey:URLKey];
    if(dispatchQueue == 0){
        dispatchQueue = dispatch_queue_create([@"kCommoneQuestQueue" UTF8String], DISPATCH_QUEUE_SERIAL);
    }
    dispatch_async(dispatchQueue, ^{
        [responseDict writeToFile:cachePath atomically:YES];
    });
}

#pragma mark - Loading
+ (void)showLoadingForType:(RequestLoadType)type inView:(nullable UIView *)view
{
    if (type == RLT_EmptyLoad) {
        [SimLoading showEmptyLoadingInView:view];
    }
    else{
        [SimLoading showOverlayLoadingInView:view];
    }
}

+ (void)dismissLoadingInView:(nullable UIView *)view
{
    [SimLoading dismissInView:view];
}


#pragma mark - util
+ (nonnull NSString *)keyForURLString:(nonnull NSString *)urlString
{
    if ([[urlString substringFromIndex:[urlString length]-1] isEqualToString:@"/"]) {
        urlString = [urlString substringToIndex:[urlString length]-1];
    }
    
    return mdsString(urlString);
}

+ (nullable NSMutableString *)queryStrWithParam:(nullable NSDictionary *)queryParam
{
    NSMutableString *queryStr = nil;
    NSUInteger count = queryParam.count;
    if (count > 0) {
        queryStr = [NSMutableString string];
        count = queryParam.count;
        for (id key in [queryParam allKeys]) {
            if ([key isKindOfClass:[NSString class]]) {
                [queryStr appendFormat:@"%@=%@%@", key, queryParam[key], count > 1? @"&" : @""];
            }
            count--;
        }
    }
    
    return queryStr;
}


@end
